//
// Created by 抑~风 on 2022/12/17.
//

#ifndef BPLUSDATABASE_LIBRARYSYS_H
#define BPLUSDATABASE_LIBRARYSYS_H

#include <ostream>
#include "../CWJBplusSE.h"
#include "../Util/timeUtil.h"
#include <string>

using namespace std;
using namespace CWJ::TIME;

namespace CWJ::LibSys {

    using namespace CWJ::BPlusSE;

    const size_t MAX_LEN = 120;
    const size_t MIDDLE_LEN = 80;
    //账号表
    // 姓名 ，年龄，性别

    template<int len>
    struct CharIndex {
        CharIndex(const char *name) {
            strcpy(this->name, name);
        }

        CharIndex(const CharIndex &rhs) { *this = rhs; }

        CharIndex() { memset(name, 0, len); }

        char name[len+1]{0};

        bool operator==(const CharIndex &rhs) const {
            return strcmp(name, rhs.name) == 0;
        }

        bool operator!=(const CharIndex &rhs) const {
            return !(rhs == *this);
        }

        bool operator<(const CharIndex &rhs) const {
            return strcmp(name, rhs.name) < 0;
        }

        CharIndex &operator=(const CharIndex &rhs) {
            memcpy(name, rhs.name, len);
            return *this;
        }

        friend ostream &operator<<(ostream &os, const CharIndex &index) {
            os << "name: " << index.name;
            return os;
        }
    };

    struct AccountIndex {
        char name[MIDDLE_LEN];
    };

    struct Account {
        enum class Type {
            ADMIN,
            USER,
        };
        char username[MIDDLE_LEN]{0};
        char password[MIDDLE_LEN]{0};
        char nickname[MIDDLE_LEN]{0};
        size_t age{0};
        bool isMan{false};
        Type type{Type::USER};

        Account(const Account &acc) {
            *this = acc;
        }

        Account() {}

        friend ostream &operator<<(ostream &os, const Account &account) {
            os << "username: " << account.username << " password: " << account.password << " nickname: "
               << account.nickname << " age: " << account.age << " isMan: " << account.isMan;
            return os;
        }

        Account(const char *username, const char *password, const char *nickname, size_t age, bool isMan, Type type)
                : age(age), isMan(isMan), type(type) {

            memcpy(this->password, password, MIDDLE_LEN);
            memcpy(this->username, username, MIDDLE_LEN);
            memcpy(this->nickname, nickname, MIDDLE_LEN);

        }

        Account &operator=(const Account &rhs) {
            memcpy(username, rhs.username, MIDDLE_LEN);
            memcpy(password, rhs.password, MIDDLE_LEN);
            memcpy(nickname, rhs.nickname, MIDDLE_LEN);
            username[MIDDLE_LEN - 1] = 0;
            password[MIDDLE_LEN - 1] = 0;
            nickname[MIDDLE_LEN - 1] = 0;
            age = rhs.age;
            isMan = rhs.isMan;
            type = rhs.type;
            return *this;
        }

    };

    // 借阅记录

    struct BrRecordIndex {
        size_t id;
    };

    struct BrRecord {
        enum class State {
            APPLYING,
            BORROWING,
            REFUSE,
            RETURN,
        };
        size_t id;
        char lender[MIDDLE_LEN]{0};
        char admin[MIDDLE_LEN]{0};
        size_t bid;
        time_t brTime;
        time_t returnTime;
        State state;

        BrRecord(const BrRecord &record) { *this = record; }

        BrRecord() {}


        BrRecord &operator=(const BrRecord &rhs) {
            id = rhs.id;
            memcpy(lender, rhs.lender, MIDDLE_LEN);
            memcpy(admin, rhs.admin, MIDDLE_LEN);
            lender[MIDDLE_LEN - 1] = 0;
            admin[MIDDLE_LEN - 1] = 0;
            bid = rhs.bid;
            brTime = rhs.brTime;
            returnTime = rhs.returnTime;
            state = rhs.state;
            return *this;
        }

        string getLender() const {
            return lender;
        }

        string getAdmin() const {
            return admin;
        }

        std::string getBrTime() const {
            return ShowDateTime(this->brTime);
        }

        std::string getReturnTime() const {
            return ShowDateTime(this->returnTime);
        }

        BrRecord(size_t id, const char *lender, const char *admin, size_t bid, time_t brTime, time_t returnTime,
                 State state) : id(
                id), bid(bid), brTime(brTime), returnTime(returnTime), state(state) {
            memcpy(this->lender, lender, MIDDLE_LEN);
            memcpy(this->admin, admin, MIDDLE_LEN);

        }

        friend ostream &operator<<(ostream &os, const BrRecord &record) {
            os << "id: " << record.id << " lender: " << record.lender << " admin: " << record.admin << " bid: "
               << record.bid << " brTime: " << record.brTime << " returnTime: " << record.returnTime << " state: "
               << (unsigned int) record.state;
            return os;
        }

    };

    // 书籍表

    struct BookIdIndex {
        //书号
        size_t id;
    };

    struct BookAuthorIndex {
        // 书名
        char bookName[MIDDLE_LEN];
    };

    struct Book {
        //书号
        size_t id;
        // 书名
        char bookName[MIDDLE_LEN]{0};
        // 作者
        char author[MIDDLE_LEN]{0};
        // 出版社
        char publishHouse[MAX_LEN]{0};


        // 分类
        // 藏书
        size_t sum;
        // 借出数目
        size_t brNum;

        size_t borrowedTimes;

        char place[MAX_LEN]{0};


        Book() {}

        Book(size_t id, const char *bookName, const char *author, const char *publishHouse, size_t sum, size_t brNum,
             size_t borrowedTimes = 0, const char *place = "") : id(id), sum(sum),
                                                                 brNum(brNum), borrowedTimes(borrowedTimes) {
            strcpy(this->bookName, bookName);
            strcpy(this->author, author);
            strcpy(this->publishHouse, publishHouse);
            strcpy(this->place, place);
        }

        Book &operator=(const Book &b) {
            id = b.id;
            strcpy(bookName, b.bookName);
            strcpy(author, b.author);
            strcpy(publishHouse, b.publishHouse);
            strcpy(place, b.place);
            bookName[MIDDLE_LEN - 1] = 0;
            author[MIDDLE_LEN - 1] = 0;
            publishHouse[MIDDLE_LEN - 1] = 0;
            place[MAX_LEN - 1] = 0;
            sum = b.sum;
            brNum = b.brNum;
            borrowedTimes = b.borrowedTimes;
            return *this;
        }

        Book(const Book &book) { *this = book; }

        friend ostream &operator<<(ostream &os, const Book &book) {
            os << "id: " << book.id << " bookName: " << book.bookName << " author: " << book.author << " publishHouse: "
               << book.publishHouse << " sum: " << book.sum << " brNum: " << book.brNum;
            return os;
        }

    };

    struct LibrarySys {

        // 账号表
        BPlusTree<CharIndex<MIDDLE_LEN>, Account> accounts{"cwjAccounts.db", false};

        // 书籍表
        BPlusTree<size_t, Book> books{"books.db", false};
        // 书名辅助索引
        BPlusTree<pair<CharIndex<MIDDLE_LEN>, size_t>, size_t> bookNameIndex{"bookNameIndex.db", false};
        // 作者辅助索引
        BPlusTree<pair<CharIndex<MIDDLE_LEN>, size_t>, std::size_t> authorIndex{"authorIndex.db", false};
        // 出版社辅助索引
        BPlusTree<pair<CharIndex<MAX_LEN>, size_t>, std::size_t> phIndex{"phIndex.db", false};

        // 借阅表

        BPlusTree<size_t, BrRecord> brRecords{"brRecords.db", false};

        void addBook(const Book &book) {
            books.insert(book.id, book);
            bookNameIndex.insert({book.bookName, book.id}, book.id);
            authorIndex.insert({book.author, book.id}, book.id);
            phIndex.insert({book.publishHouse, book.id}, book.id);
        }

        void removeBook(const Book &book) {
            books.remove(book.id);
            bookNameIndex.remove({book.bookName, book.id});
            authorIndex.remove({book.author, book.id});
            phIndex.remove({book.publishHouse, book.id});
        }

        Book getBook(size_t id) {
            Book book;
            this->books.searchKey(id, book);
            return book;
        }

        vector<Book> getBooksByBookName(const char *name) {
            vector<Book> list;
            for (auto itr = this->bookNameIndex.searchRange({name, 0}); itr != this->bookNameIndex.end(); ++itr) {
                size_t id = itr->val;
                auto book = this->getBook(id);
                if (strcmp(book.bookName, name) == 0) {
                    list.push_back(book);
                } else break;
            }
            return list;
        }

        vector<Book> getBooksByAuthor(const char *authorName) {
            vector<Book> list;
            for (auto itr = this->authorIndex.searchRange({authorName, 0}); itr != this->authorIndex.end(); ++itr) {
                size_t id = itr->val;
                auto book = this->getBook(id);
                if (strcmp(book.author, authorName) == 0) {
                    list.push_back(book);
                } else break;
            }
            return list;
        }

        vector<Book> getBooksByPublisher(const char *ph) {
            vector<Book> list;
            for (auto itr = this->phIndex.searchRange({ph, 0}); itr != this->phIndex.end(); ++itr) {
                size_t id = itr->val;
                auto book = this->getBook(id);
                if (strcmp(book.publishHouse, ph) == 0) {
                    list.push_back(book);
                } else break;
            }
            return list;
        }


        void acceptApply(size_t id, const Account &acc) {
            BrRecord rec;
            this->brRecords.searchKey(id, rec);
            rec.state = BrRecord::State::BORROWING;
            rec.brTime = std::time(0);
            strcpy(rec.admin, acc.username);
            this->brRecords.insert(id, rec);

            Book book;
            this->books.searchKey(rec.bid, book);
            book.brNum++;
            book.borrowedTimes++;
            this->books.insert(rec.bid, book);
        }

        void refuseApply(size_t id, const Account &acc) {
            BrRecord rec;
            this->brRecords.searchKey(id, rec);
            rec.state = BrRecord::State::REFUSE;
            rec.returnTime = std::time(0);
            strcpy(rec.admin, acc.username);
            this->brRecords.insert(id, rec);
        }

        void addApply(const Account &acc, size_t bookId) {
            size_t brRecordsLastId = 0;
            for(auto itr : brRecords){
                brRecordsLastId = itr.key;
            }
            brRecordsLastId++;
            this->brRecords.insert(brRecordsLastId,
                                   BrRecord{brRecordsLastId, acc.username, "", bookId,
                                    std::time(0), std::time(0),
                                    BrRecord::State::APPLYING});
        }


        void returnBook(size_t id) {
            BrRecord rec;
            this->brRecords.searchKey(id, rec);
            rec.state = BrRecord::State::RETURN;
            rec.brTime = std::time(0);
            this->brRecords.insert(id, rec);

            Book book;
            this->books.searchKey(rec.bid, book);
            book.brNum--;
            this->books.insert(rec.bid, book);
        }

        void addAcc(const Account &acc) {
            this->accounts.insert(acc.username, acc);
        }

        Account getAcc(const char *username) {
            Account acc;
            this->accounts.searchKey(username, acc);
            return acc;
        }

        void removeAcc(const Account &acc) {
            this->accounts.remove(acc.username);
        }

        void removeAcc(const char *username) {
            this->accounts.remove(username);
        }


    };


}

#endif //BPLUSDATABASE_LIBRARYSYS_H
